package org.twndmv.blimpl;

import gov.ojp.it.jxdm._3_0.PersonPhysicalDetailsType;
import gov.ojp.it.jxdm._3_0.ResidenceType;
import info.ejava.projects.edmv._1.Dmv;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.xml.bind.JAXBException;
import javax.xml.stream.XMLStreamException;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.twndmv.bl.DmvIngestor;
import org.twndmv.bo.Color;
import org.twndmv.bo.Person;
import org.twndmv.bo.PhysicalDetails;
import org.twndmv.bo.Residence;
import org.twndmv.bo.Sex;
import org.twndmv.bo.State;
import org.twndmv.bo.VehicleRegistration;
import org.twndmv.dao.PersonDAO;
import org.twndmv.dao.VehicleRegistrationDAO;
import org.twndmv.exception.DmvException;
import org.twndmv.exception.NoSuchColorException;

import ejava.projects.edmv.xml.EDmvParser;

/**
 * This class provides a _sparse_ example implementation of how one can use the
 * parser to ingest the information from the XML file to populate the database.
 * 
 * Class was taken from EDmvIngestor example off class source code.
 * 
 */
public class DmvIngestorImpl implements DmvIngestor {
	private static final Log log = LogFactory.getLog(DmvIngestorImpl.class);
	InputStream is;
	PersonDAO personDAO;
	VehicleRegistrationDAO vehicleDAO;
	EDmvParser parser;
	Map<String, Person> dto2bo = new HashMap<String, Person>();

	@Override
	public void setInputStream(InputStream is) {
		this.is = is;
	}

	@Override
	public void setPersonDAO(PersonDAO personDAO) {
		this.personDAO = personDAO;
	}

	@Override
	public void setVehicleDAO(VehicleRegistrationDAO vehicleDAO) {
		this.vehicleDAO = vehicleDAO;
	}

	/**
	 * This method will ingest the input data by reading in external DTOs in
	 * from the parser, instantiating project business objects, and inserting
	 * into database. Note that the XML Schema is organized such that object
	 * references are fully resolved. Therefore, there is no specific need to
	 * process the addresses as they come in. They can be stored once we get the
	 * accounts they are related to.
	 * 
	 * @throws JAXBException
	 * @throws XMLStreamException
	 * @throws DAOException
	 */
	@Override
	public void ingest() throws JAXBException, XMLStreamException, Exception {
		EDmvParser parser = new EDmvParser(Dmv.class, is);

		Object object = parser.getObject("Person", "VehicleRegistration");
		while (object != null) {
			if (object instanceof gov.ojp.it.jxdm._3_0.Person) {
				createPerson((gov.ojp.it.jxdm._3_0.Person) object);
			}
			if (object instanceof gov.ojp.it.jxdm._3_0.VehicleRegistration) {
				createRegistration((gov.ojp.it.jxdm._3_0.VehicleRegistration) object);
			}
			object = parser.getObject("Person", "VehicleRegistration");
		}
	}

	/**
	 * This method is called by the main ingest processing loop. The JAXB/StAX
	 * parser will already have the Person populated with internal property
	 * information.
	 * 
	 * @param personDTO
	 * @throws DAOException
	 */
	private void createPerson(gov.ojp.it.jxdm._3_0.Person personDTO)
			throws Exception {
		Person personBO = new Person();
		personBO.setFirstName(personDTO.getPersonName().getPersonGivenName().getValue());
		personBO.setLastName(personDTO.getPersonName().getPersonSurName()
				.getValue());
		personBO.setMiddleName(personDTO.getPersonName().getPersonMiddleName().getValue());
		PhysicalDetails details = createPhysicalDetails(personDTO.getPersonPhysicalDetails());
		details.setDob(personDTO.getPersonBirthDate().getValue().toGregorianCalendar().getTime());

		personBO.setPhysicalDetails(details);
		
		for(ResidenceType house : personDTO.getResidence()) {
			Residence residence = new Residence();
			residence.setCity(house.getLocationAddress().getLocationCityName().getValue());
			residence.setState(State.valueOf(house.getLocationAddress().getLocationStateCodeUSPostalService().getValue()));
			residence.setStreetName(house.getLocationAddress().getLocationStreet().getStreetName().getValue());
			residence.setStreetNo(Integer.parseInt(house.getLocationAddress().getLocationStreet().getStreetNumberText().getValue()));
			residence.setZipCode(Integer.parseInt(house.getLocationAddress().getLocationPostalCodeID().getID().getValue()));
			residence.setMoveIn(house.getResidenceStartDate().getValue().toGregorianCalendar().getTime());
			if(house.getResidenceEndDate().getValue() == null) {
				residence.setMoveOut(null);
				personBO.setCurrentResidence(residence);		
			} else {
			  residence.setMoveOut(house.getResidenceEndDate().getValue().toGregorianCalendar().getTime());	
			}
			personBO.getResidences().add(residence);
		}
		
		personDAO.createPerson(personBO);
		log.debug("created person:" + personBO);

		// map DTO id to BO object for follow-on ownership processing
		dto2bo.put(personDTO.getId(), personBO);
	}

	private PhysicalDetails createPhysicalDetails(
			PersonPhysicalDetailsType personPhysicalDetails) {
		PhysicalDetails results = new PhysicalDetails();
		try {
		results.setEyeColor(Color.translate(personPhysicalDetails.getPersonEyeColorCode().getValue()));
		} catch(NoSuchColorException nsce) {
			log.error("Color " + personPhysicalDetails.getPersonEyeColorCode().getValue() + " does not exist" );
		}
		try {
		results.setHairColor(Color.translate(personPhysicalDetails.getPersonHairColorText().getValue()));
		} catch(NoSuchColorException nsce) {
			log.error("Color " + personPhysicalDetails.getPersonEyeColorCode().getValue() + " does not exist" );
		}
		if(personPhysicalDetails.getPersonDigitalImage() != null) {
		  results.setPhoto(personPhysicalDetails.getPersonDigitalImage().getBinaryObjectBase64().getValue());
		}
		results.setHeight(personPhysicalDetails.getPersonHeightMeasure().getValue().intValue());
		results.setWeight(personPhysicalDetails.getPersonWeightMeasure().getValue().intValue());
		results.setSEX(Sex.translate(personPhysicalDetails.getPersonSexCode().getValue()));
		return results;
	}

	/**
	 * This method is called by the main ingest processing loop. The JAXB/StAX
	 * parser will already have the VehicleRegistration populated with internal
	 * property information. The Person information for owners should also
	 * already exist.
	 * 
	 * @param registrationDTO
	 * @throws DAOException
	 */
	@SuppressWarnings("deprecation")
	private void createRegistration(
			gov.ojp.it.jxdm._3_0.VehicleRegistration registrationDTO)
			throws Exception {
		VehicleRegistration registrationBO = new VehicleRegistration();
		Date date = new Date();
		date.setMonth(registrationDTO.getVehicleRegistrationDecal().getDecalMonthDate().getValue().getMonth());
		date.setYear(registrationDTO.getVehicleRegistrationDecal().getDecalYearDate().getValue().getYear());
		registrationBO.setExpiration(date);
		registrationBO.setMake(registrationDTO.getVehicle().getVehicleMakeCode().getValue());
		registrationBO.setModel(registrationDTO.getVehicle().getVehicleModelCode().getValue());
		try {
		registrationBO.setColor(Color.translate((registrationDTO.getVehicle().getVehicleColorPrimaryCode().getValue())));
		} catch(NoSuchColorException nsce) {
			log.error("Color " + registrationDTO.getVehicle().getVehicleColorPrimaryCode().getValue() + " does not exist" );
		}
		registrationBO.setYear(registrationDTO.getVehicle().getVehicleModelYearDate().getValue().getYear());
		registrationBO.setTagNo(registrationDTO.getVehicleLicensePlateID().getID().getValue());
		registrationBO.setVin(registrationDTO.getVehicle().getVehicleID()
				.getID().getValue());

		
		List<Person> updatedPeople = new ArrayList<Person>();
		// add the m-m owners
		for (gov.ojp.it.jxdm._3_0.ReferenceType ref : registrationDTO
				.getVehicle().getPropertyOwnerPerson()) {
			gov.ojp.it.jxdm._3_0.Person ownerDTO = (gov.ojp.it.jxdm._3_0.Person) ref
					.getRef();
			Person ownerBO = dto2bo.get(ownerDTO.getId());
			if (ownerBO == null) {
				throw new DmvException("owner not found:" + ownerDTO.getId());
			}
            Person savedPerson = personDAO.getPersonByID(ownerBO.getId());
			savedPerson.getRegistrations().add(registrationBO);
            updatedPeople.add(savedPerson);
			registrationBO.getOwners().add(savedPerson);
		}
		
		vehicleDAO.createRegistration(registrationBO);
		for(Person person : updatedPeople) {
	          personDAO.updatePerson(person);
			}
		log.debug("created registration:" + registrationBO);
	}

}
